package org.example.ipersistent.sqlSession;

import org.example.ipersistent.executor.Executor;
import org.example.ipersistent.pojo.Configuration;
import org.example.ipersistent.pojo.MappedStatement;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Objects;

public class DefaultSqlSession implements SqlSession {
  private Configuration configuration;
  private Executor executor;
  private String namespace;

  public DefaultSqlSession(Configuration configuration, Executor executor, String namespace) {
    this.configuration = configuration;
    this.executor = executor;
    this.namespace = namespace;
  }

  @Override
  public List selectList() {
    String statementId = namespace + ":selectAll";
    MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
    return executor.query(configuration, mappedStatement, null);
  }

  @Override
  public Object selectOne(Object o) {
    String statementId = namespace + ":selectOne";
    MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
    List list = executor.query(configuration, mappedStatement, o);
    if (list.isEmpty()) {
      return new Object();
    } else if (list.size() > 1) {
      throw new RuntimeException("out bound" + list.size() + ",except 1");
    } else {
      return list.get(0);
    }
  }

  @Override
  public void close() {
    executor.close();
  }

  @Override
  public Object getMapper(Class<?> mapperClass) {
    return Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[]{mapperClass}, (proxy, method, args) -> {
      String methodName = method.getName();
      String className = method.getDeclaringClass().getName();
      String statementId = className + ":" + methodName;
      MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
      if (Objects.equals(mappedStatement.getCommandType(), "select")) {
        Type genericReturnType = method.getGenericReturnType();
        Object arg = args == null ? null : args[0];
        List list = executor.query(configuration, mappedStatement, arg);
        if (genericReturnType instanceof ParameterizedType) {
          return list;
        }
        if (list.isEmpty()) {
          return new Object();
        } else if (list.size() > 1) {
          throw new RuntimeException("out bound" + list.size() + ",except 1");
        } else {
          return list.get(0);
        }
      }
      // insert update delete
      return null;
    });
  }
}
